package com.yytek.job.service;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import cn.hutool.core.collection.CollectionUtil;
import com.yytek.common.core.constant.CacheConstants;
import com.yytek.common.core.utils.CronUtil;
import com.yytek.common.redis.service.RedisService;
import com.yytek.job.domain.PlanTaskTemplate;
import com.yytek.job.mapper.PlanTaskTemplateMapper;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.yytek.common.core.constant.ScheduleConstants;
import com.yytek.common.core.exception.job.TaskException;
import com.yytek.job.domain.SysJob;
import com.yytek.job.mapper.SysJobMapper;
import com.yytek.job.util.CronUtils;
import com.yytek.job.util.ScheduleUtils;

/**
 * 定时任务调度信息 服务层
 * 
 * @author ruoyi
 */
@Service
public class SysJobServiceImpl implements ISysJobService
{
    @Autowired
    private Scheduler scheduler;
    @Autowired
    private RedisService redisService;
    @Autowired
    private SysJobMapper jobMapper;
    @Autowired
    private PlanTaskTemplateMapper taskTemplateMapper;
    private PlanTaskTemplateServiceImpl planTaskTemplateService;// 去掉@Autowired

    // 采用构造器方式，加入Lazy注解
    public SysJobServiceImpl(@Lazy PlanTaskTemplateServiceImpl taskTemplateService) {
        this.planTaskTemplateService = taskTemplateService;
    }

    /**
     * 项目启动时，初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     */
    @PostConstruct
    public void init() throws SchedulerException, TaskException
    {
        scheduler.clear();
        List<SysJob> jobList = jobMapper.selectJobAll();
        for (SysJob job : jobList)
        {
            //如果不是只执行一次时间
            if(CronUtil.isTimeCron(job.getCronExpression())){
                //如果是过期当前时间
                if(CronUtil.isBeforeNowTime(job.getCronExpression())){
                    continue;
                }else{
                    ScheduleUtils.createScheduleJob(scheduler, job);
                }
            }else{
                ScheduleUtils.createScheduleJob(scheduler, job);
            }
        }
    }

    /**
     * 获取quartz调度器的计划任务列表
     * 
     * @param job 调度信息
     * @return
     */
    @Override
    public List<SysJob> selectJobList(SysJob job)
    {
        return jobMapper.selectJobList(job);
    }

    /**
     * 通过调度任务ID查询调度信息
     * 
     * @param jobId 调度任务ID
     * @return 调度任务对象信息
     */
    @Override
    public SysJob selectJobById(Long jobId)
    {
        return jobMapper.selectJobById(jobId);
    }

    /**
     * 暂停任务
     * 
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int pauseJob(SysJob job) throws SchedulerException
    {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = jobMapper.updateJob(job);
        if (rows > 0)
        {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }

    /**
     * 恢复任务
     * 
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int resumeJob(SysJob job) throws SchedulerException
    {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        int rows = jobMapper.updateJob(job);
        if (rows > 0)
        {
            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }

    /**
     * 删除任务后，所对应的trigger也将被删除
     * 
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteJob(SysJob job) throws SchedulerException
    {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        int rows = jobMapper.deleteJobById(jobId);
        if (rows > 0)
        {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }

    /**
     * 删除设备所有的任务
     * @param deviceId
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void delteDeviceJob(String deviceId) throws SchedulerException {
        SysJob cx = new SysJob();
        cx.setDeviceId(deviceId);
        List<SysJob> sysJobs = jobMapper.selectJobList(cx);
        if(CollectionUtil.isNotEmpty(sysJobs)){
            for (int i = 0; i < sysJobs.size(); i++) {
                SysJob job = sysJobs.get(i);
                deleteJob(job);
            }
        }
    }
    /**
     * 批量删除调度信息
     * 
     * @param jobIds 需要删除的任务ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException
    {
        for (Long jobId : jobIds)
        {
            SysJob job = jobMapper.selectJobById(jobId);
            deleteJob(job);
        }
    }

    /**
     * 批量更改设备任务状态
     * @param jobList
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeDeviceNoStateTask(List<SysJob> jobList) throws SchedulerException {
        int rows = 0;
        List<SysJob> changList = new ArrayList<>();
        //批量查询设备所有任务,添加进设备任务
        for (int i = 0; i < jobList.size(); i++) {
            SysJob cxsysJob = jobList.get(i);
            //查询该设备的非状态采集任务
            List<SysJob> list = jobMapper.selectJobListByDeviceIdAndNoState(cxsysJob);
            changList.addAll(list);
        }
        //批量任务调度修改
        rows = changeListStatus(changList);
    }



    /**
     * 批量任务调度状态修改
     * @param changList
     */
    private int changeListStatus(List<SysJob> changList) throws SchedulerException {
        if(CollectionUtil.isNotEmpty(changList)){
            //开始恢复任务列表
            List<SysJob> resumeJobList = new ArrayList<>();
            //暂停任务列表
            List<SysJob> pauseJobList = new ArrayList<>();
            //循环添加进队列批量处理
            for (int i = 0; i < changList.size(); i++) {
                SysJob tempJob = changList.get(i);
                String status = tempJob.getStatus();
                if (ScheduleConstants.Status.NORMAL.getValue().equals(status))
                {
                    resumeJobList.add(tempJob);
                }
                else if (ScheduleConstants.Status.PAUSE.getValue().equals(status))
                {
                    pauseJobList.add(tempJob);
                }
            }
            //批量处理队列
            int resumeRows = resumeJobList(resumeJobList);
            int pauseRows = pauseJobList(pauseJobList);
            return resumeRows+pauseRows;
        }else {
            return 0;
        }
    }
    /**
     * 批量暂停任务队列
     * @param pauseJobList
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int pauseJobList(List<SysJob> pauseJobList) throws SchedulerException {
        int rows = jobMapper.updateJobList(pauseJobList);
        if (rows == pauseJobList.size())
        {
            for (int i = 0; i < pauseJobList.size(); i++) {
                SysJob job = pauseJobList.get(i);
                Long jobId = job.getJobId();
                String jobGroup = job.getJobGroup();
                scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
            }
        }else{
            throw new RuntimeException("批量恢复任务失败,暂停数量不一致");
        }
        return rows;
    }

    /**
     * 批量恢复任务队列
     * @param resumeJobList
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int resumeJobList(List<SysJob> resumeJobList) throws SchedulerException {
        int rows = jobMapper.updateJobList(resumeJobList);
        if (rows == resumeJobList.size())
        {
            for (int i = 0; i < resumeJobList.size(); i++) {
                SysJob job = resumeJobList.get(i);
                Long jobId = job.getJobId();
                String jobGroup = job.getJobGroup();
                scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
            }
        }else{
            throw new RuntimeException("批量恢复任务失败,更新数量不一致");
        }
        return rows;
    }

    /**
     * 任务调度状态修改
     * 
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int changeStatus(SysJob job) throws SchedulerException
    {
        int rows = 0;
        String status = job.getStatus();
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status))
        {
            rows = resumeJob(job);
        }
        else if (ScheduleConstants.Status.PAUSE.getValue().equals(status))
        {
            rows = pauseJob(job);
        }
        return rows;
    }

    /**
     * 立即运行任务
     * 
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void run(SysJob job) throws SchedulerException
    {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        SysJob properties = selectJobById(job.getJobId());
        // 参数
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
        scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
    }

    /**
     * 创建设备的任务
     * @param projectId
     * @param productId
     * @param deviceId
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public int createDeviceTaskJob(Long projectId, Long productId, String deviceId) throws Exception {
        //查询任务模板
        PlanTaskTemplate cxtemplate = new PlanTaskTemplate();
        cxtemplate.setProjectId(projectId);
        cxtemplate.setProductId(productId);
        List<PlanTaskTemplate> taskTemplates = taskTemplateMapper.selectPlanTaskTemplateList(cxtemplate);

        if(CollectionUtil.isNotEmpty(taskTemplates)){
            for (int i = 0; i < taskTemplates.size(); i++) {
                //根据任务模板创建不同设备任务
                planTaskTemplateService.createNewDeviceTask(taskTemplates.get(i),deviceId);
            }
        }
        return 0;
    }



    /**
     * 批量新增任务
     * @param deviceJobs
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int insertJobList(List<SysJob> deviceJobs) throws Exception {
        int zongRows = 0;
        int rows = jobMapper.insertJobList(deviceJobs);
        for (int i = 0; i < deviceJobs.size(); i++) {
            SysJob job = deviceJobs.get(i);
            job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
            if (rows > 0)
            {
                ScheduleUtils.createScheduleJob(scheduler, job);
                zongRows = zongRows+1;
            }
        }
        return zongRows;
    }
    /**
     * 新增任务
     * 
     * @param job 调度信息 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertJob(SysJob job) throws SchedulerException, TaskException
    {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = jobMapper.insertJob(job);
        if (rows > 0)
        {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return rows;
    }

    /**
     * 更新任务的时间表达式
     * 
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateJob(SysJob job) throws SchedulerException, TaskException
    {
        SysJob properties = selectJobById(job.getJobId());
        int rows = jobMapper.updateJob(job);
        if (rows > 0)
        {
            updateSchedulerJob(job, properties.getJobGroup());
        }
        return rows;
    }

    /**
     * 更新任务
     * 
     * @param job 任务对象
     * @param jobGroup 任务组名
     */
    public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException
    {
        Long jobId = job.getJobId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey))
        {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }

    /**
     * 校验cron表达式是否有效
     * 
     * @param cronExpression 表达式
     * @return 结果
     */
    @Override
    public boolean checkCronExpressionIsValid(String cronExpression)
    {
        return CronUtils.isValid(cronExpression);
    }

    /**
     * 获取设备任务缓存
     * @param taskTemplateId 任务模板id
     * @param iotId 设备id
     * @return
     */
    public SysJob getSysJobCache(String taskTemplateId,String iotId) {
        String key = CacheConstants.TASK_JOB_INFO + taskTemplateId + iotId;
        if(redisService.hasKey(key)){
            return (SysJob)redisService.getCacheObject(key);
        }else{
            SysJob cxjob = new SysJob();
            cxjob.setDeviceId(iotId);
            cxjob.setTemplateId(Long.valueOf(taskTemplateId));
            List<SysJob> result = selectJobList(cxjob);
            if(CollectionUtil.isNotEmpty(result)){
                SysJob job = result.get(0);
                redisService.setCacheObject(key,job);
                return job;
            }
            return null;
        }
    }
}